package com.hmall.item.service.impl;

import cn.hutool.core.util.StrUtil;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.utils.CollUtils;
import com.hmall.item.domain.po.ItemDoc;
import com.hmall.item.domain.query.ItemPageQuery;
import com.hmall.item.service.IItemSearchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ItemSearchServiceImpl implements IItemSearchService {

    private static final String INDEX_NAME = "items";
    private final RestHighLevelClient esClient;

    @Override
    public PageDTO<ItemDoc> search(ItemPageQuery query) {
        try {
            // 1.准备请求
            SearchRequest request = new SearchRequest(INDEX_NAME);
            
            // 2.准备DSL
            // 2.1.构建查询条件
            buildBasicQueryDSL(request, query);
            
            // 2.2.分页
            int page = query.getPageNo();
            int size = query.getPageSize();
            request.source().from((page - 1) * size).size(size);
            
            // 2.3.排序
            if (StrUtil.isNotBlank(query.getSortBy())) {
                request.source().sort(query.getSortBy(), 
                        query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
            }
            
            // 2.4.高亮
            if (StrUtil.isNotBlank(query.getKey())) {
                request.source().highlighter(
                    new HighlightBuilder()
                        .field("name")
                        .preTags("<em>")
                        .postTags("</em>")
                        .requireFieldMatch(false)
                );
            }
            
            // 3.发送请求
            SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
            
            // 4.解析响应
            return handleResponse(response, query.getPageNo(), query.getPageSize());
        } catch (IOException e) {
            log.error("查询商品异常", e);
            return PageDTO.empty(Long.valueOf(query.getPageNo()), Long.valueOf(query.getPageSize()));
        }
    }
    
    private void buildBasicQueryDSL(SearchRequest request, ItemPageQuery query) {
        // 1.构建基础查询条件（BoolQuery）
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        
        // 2.关键字搜索
        if (StrUtil.isNotBlank(query.getKey())) {
            boolQuery.must(QueryBuilders.matchQuery("name", query.getKey()));
        } else {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        
        // 3.商品分类过滤，使用keyword子字段进行精确匹配
        if (StrUtil.isNotBlank(query.getCategory())) {
            boolQuery.filter(QueryBuilders.termQuery("category.keyword", query.getCategory()));
        }
        
        // 4.品牌过滤，使用keyword子字段进行精确匹配
        if (StrUtil.isNotBlank(query.getBrand())) {
            boolQuery.filter(QueryBuilders.termQuery("brand.keyword", query.getBrand()));
        }
        
        // 5.价格区间过滤
        if (query.getMinPrice() != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
        }
        if (query.getMaxPrice() != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
        }
        
        // 6.使用FunctionScore查询提升广告商品的排名
        // 6.1 创建weight函数，对isAD=true的文档加权
        FunctionScoreQueryBuilder.FilterFunctionBuilder adFunction = 
            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                QueryBuilders.termQuery("isAD", true),  // 过滤条件：isAD为true的文档
                ScoreFunctionBuilders.weightFactorFunction(10.0f)  // 权重提升10倍
            );
        
        // 6.2 创建functionScore查询
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                boolQuery,  // 原始查询
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{adFunction}  // 函数数组
            )
            .boostMode(CombineFunction.MULTIPLY)  // 设置分数组合模式为相乘
            .maxBoost(100.0f);  // 设置最大加权值
        
        // 7.添加查询条件到请求
        request.source().query(functionScoreQuery);
    }

    private PageDTO<ItemDoc> handleResponse(SearchResponse response, int page, int size) {
        // 1.获取搜索结果
        SearchHits searchHits = response.getHits();
        // 2.获取总条数
        long total = searchHits.getTotalHits().value;
        // 3.获取文档数组
        SearchHit[] hits = searchHits.getHits();
        
        // 4.判断是否有搜索结果
        if (hits.length <= 0) {
            return PageDTO.empty(Long.valueOf(total), (long)Math.ceil((double) total / size));
        }
        
        // 5.解析搜索结果
        List<ItemDoc> items = new ArrayList<>(hits.length);
        for (SearchHit hit : hits) {
            // 5.1.获取文档source
            String json = hit.getSourceAsString();
            // 5.2.反序列化
            ItemDoc itemDoc = cn.hutool.json.JSONUtil.toBean(json, ItemDoc.class);
            
            // 5.3.处理高亮结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (CollUtils.isNotEmpty(highlightFields)) {
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null && highlightField.getFragments() != null && highlightField.getFragments().length > 0) {
                    // 使用高亮结果替换原始内容
                    itemDoc.setName(highlightField.getFragments()[0].string());
                }
            }
            
            // 5.4.添加到结果集合
            items.add(itemDoc);
        }
        
        // 6.封装返回
        return new PageDTO<>(total, (long)Math.ceil((double) total / size), items);
    }

    @Override
    public Map<String, List<String>> filters(ItemPageQuery query) {
        try {
            // 1.准备请求
            SearchRequest request = new SearchRequest(INDEX_NAME);
            
            // 2.准备DSL
            // 2.1.构建查询条件
            buildBasicQueryDSL(request, query);
            
            // 2.2.设置聚合条件
            // 2.2.1.分类聚合
            request.source().aggregation(
                AggregationBuilders
                    .terms("categoryAgg")
                    .field("category.keyword")
                    .size(100)
            );
            
            // 2.2.2.品牌聚合
            request.source().aggregation(
                AggregationBuilders
                    .terms("brandAgg")
                    .field("brand.keyword")
                    .size(100)
            );
            
            // 2.3.结果不需要文档，只需要聚合结果
            request.source().size(0);
            
            // 3.发送请求
            SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
            
            // 4.解析聚合结果
            Map<String, List<String>> result = handleAggregation(response);
            
            return result;
        } catch (IOException e) {
            log.error("查询聚合异常", e);
            return new HashMap<>();
        }
    }
    
    /**
     * 处理聚合结果
     * @param response ES响应
     * @return 聚合结果
     */
    private Map<String, List<String>> handleAggregation(SearchResponse response) {
        Map<String, List<String>> result = new HashMap<>(2);
        
        // 获取聚合结果
        Aggregations aggregations = response.getAggregations();
        if (aggregations == null) {
            return result;
        }
        
        // 解析分类聚合结果
        Terms categoryTerms = aggregations.get("categoryAgg");
        List<String> categories = categoryTerms.getBuckets().stream()
                .map(Terms.Bucket::getKeyAsString)
                .collect(Collectors.toList());
        result.put("category", categories);
        
        // 解析品牌聚合结果
        Terms brandTerms = aggregations.get("brandAgg");
        List<String> brands = brandTerms.getBuckets().stream()
                .map(Terms.Bucket::getKeyAsString)
                .collect(Collectors.toList());
        result.put("brand", brands);
        
        return result;
    }
} 